// Initializes the "Submitted to QC" and "QC Fulfilled Date" fields
global class InitNationalCaseFields implements Database.Batchable<sObject>{

    // Run this job using an anonymous call. Example:
    // id batchinstanceid = database.executeBatch(new InitNationalCaseFields(-1, false)); 
    private final integer MaxRowsToQuery;
    private final boolean ModifyObjects;
    private final Map<string, ID> queueMap;
    private final Map<Id, User> ovTeam;
    private final Map<Id, User> ssTeam;
	private final ID billingUser;
	    
    global InitNationalCaseFields(integer maxRowsToQuery, boolean modifyObjects)   
    {
        this.MaxRowsToQuery = maxRowsToQuery;
        this.ModifyObjects = modifyObjects;
        
        queueMap = new Map<string, ID>();
        
        Set<Group> queues = new Set<Group>([select ID, Name from Group where Type = 'Queue']);
        for(Group g: queues)
        	queueMap.put(g.Name, g.ID);
        	
        ovTeam = new Map<Id, User>([select Id, Name from User where IsActive = true and UserRole.Name = 'Order Validation']);
        ssTeam = new Map<Id, User>([select Id, Name from User where IsActive = true and UserRole.Name = 'Sales/Customer Support']);
        
        billingUser = [select id from user where name = 'Vijaya Veeramachaneni' limit 1][0].id;        	        
    }
    
    global Database.Querylocator start(Database.BatchableContext bc){

        List<ID> recordTypes = new List<ID>();
                
        recordTypes.add(RecordTypeHelper.GetRecordTypeId('Case', 'National Ops - Insertion Order'));
        recordTypes.add(RecordTypeHelper.GetRecordTypeId('Case', 'National Ops - Change Request'));
        string query = 'Select ID, Released_Live_Date__c, Second_QC_Check__c, Query_Counter__c, Last_Query_Date__c, Billing_Query_Counter__c, Edit_Counter__c, Last_Response_Date__c, Ready_for_AP_Date__c, Ready_for_OE_Date__c, Ready_for_QC_Date__c, ownerId, CreatedDate, Future_Start_Date__c, Keyed_In_AF__c, Status, Order_Entry__c, Quality_Control__c, Ad_Production__c,  CaseNumber from Case where RecordTypeID in :recordTypes order by CaseNumber ASC';

        if(maxRowsToQuery > 0)
            query += ' LIMIT ' + MaxRowsToQuery;
            
        return Database.getQueryLocator(query);
    }
            
    global void execute(Database.BatchableContext bc, List<sObject> scope){
       	       
        List<ID> CaseIds = new List<ID>();

        Map<ID, decimal> queryCounter = new Map<ID, decimal>();
        Map<ID, decimal> billingQueryCounter = new Map<ID, decimal>();
        Map<ID, decimal> editCounter = new Map<ID, decimal>();

        Map<ID, DateTime> releasedLive = new Map<ID, DateTime>();
        Map<ID, DateTime> secondQCCheck = new Map<ID, DateTime>();
        Map<ID, DateTime> lastQueryDate = new Map<ID, DateTime>();
        Map<ID, DateTime> lastResponseDate = new Map<ID, DateTime>();
        Map<ID, DateTime> readyforAPdate = new Map<ID, DateTime>();
        Map<ID, DateTime> readyforOEdate = new Map<ID, DateTime>();
        Map<ID, DateTime> readyforQCdate = new Map<ID, DateTime>();
        
        for(sObject s : scope)                      
            CaseIds.add(s.ID);
                
        List<CaseHistory> chList = [Select NewValue, CreatedDate, CaseId From CaseHistory where Field = 'Status' and CaseId in :CaseIds order by CreatedDate Asc];
        
        // Add the first qualifying history event to the map. 
        for(CaseHistory ch : chList)
        {
            if(ch.NewValue == 'Released Live' && !releasedLive.containsKey(ch.CaseId))
                releasedLive.put(ch.CaseId, ch.CreatedDate);               
            
            if(ch.NewValue == '2nd QC Check' && !secondQCCheck.containsKey(ch.CaseId))
                secondQCCheck.put(ch.CaseId, ch.CreatedDate);               

            if((ch.NewValue == 'OE Query') || (ch.NewValue == 'AP Query') ||
             (ch.NewValue == 'QC Query') || (ch.NewValue == 'QC to SS for Edit')){
              
                  if(queryCounter.containsKey(ch.CaseId))
                    queryCounter.put(ch.CaseId, queryCounter.get(ch.CaseId) + 1);               
                  else
                    queryCounter.put(ch.CaseId, 1);               

                lastQueryDate.put(ch.CaseId, ch.CreatedDate);               
             }

            if(ch.NewValue == 'Billing Query'){              
                  if(billingQueryCounter.containsKey(ch.CaseId))
                    billingQueryCounter.put(ch.CaseId, billingQueryCounter.get(ch.CaseId) + 1);               
                  else
                    billingQueryCounter.put(ch.CaseId, 1);               

                lastQueryDate.put(ch.CaseId, ch.CreatedDate);               
             }

            if((ch.NewValue == 'QC to OE for Edit') || (ch.NewValue == 'QC to AP for Edit')){              
                  if(editCounter.containsKey(ch.CaseId))
                    editCounter.put(ch.CaseId, editCounter.get(ch.CaseId) + 1);               
                  else
                    editCounter.put(ch.CaseId, 1);               
					
                lastQueryDate.put(ch.CaseId, ch.CreatedDate);               
            }

            if(ch.NewValue == 'Billing Query Resolved')
                lastResponseDate.put(ch.CaseId, ch.CreatedDate);               

            if(ch.NewValue == 'Ready for AP' && !readyforAPdate.containsKey(ch.CaseId))
                readyforAPdate.put(ch.CaseId, ch.CreatedDate);               

            if(ch.NewValue == 'Ready for OE' && !readyforOEdate.containsKey(ch.CaseId))
                readyforOEdate.put(ch.CaseId, ch.CreatedDate);               

            if(ch.NewValue == 'Ready for QC' && !readyforQCdate.containsKey(ch.CaseId))
                readyforQCdate.put(ch.CaseId, ch.CreatedDate);               
        }



        List<MigratedCaseFields > casesToUpdate = new List<MigratedCaseFields >();
        
        // For each case, if it has a history in the map save the value to the record
        for(sObject s : scope){
            Case soqlCase = (Case)s;

            MigratedCaseFields updatedCase = new MigratedCaseFields();
            updatedCase.ID = soqlCase.ID;
            
            if(releasedLive.containsKey(s.ID) && (soqlCase.Released_Live_Date__c != releasedLive.get(s.ID)))           
                updatedCase.Released_Live_Date = releasedLive.get(s.ID);

            if(secondQCCheck.containsKey(s.ID) && (soqlCase.Second_QC_Check__c != secondQCCheck.get(s.ID)))           
                updatedCase.Second_QC_Check = secondQCCheck.get(s.ID);

            if(queryCounter.containsKey(s.ID) && (soqlCase.Query_Counter__c != queryCounter.get(s.ID)))           
                updatedCase.Query_Counter = queryCounter.get(s.ID);

            if(lastQueryDate.containsKey(s.ID) && (soqlCase.Last_Query_Date__c != lastQueryDate.get(s.ID)))           
                updatedCase.Last_Query_Date = lastQueryDate.get(s.ID);

            if(billingQueryCounter.containsKey(s.ID) && (soqlCase.Billing_Query_Counter__c != billingQueryCounter.get(s.ID)))           
                updatedCase.Billing_Query_Counter = billingQueryCounter.get(s.ID);

            if(editCounter.containsKey(s.ID) && (soqlCase.Edit_Counter__c != editCounter.get(s.ID)))           
                updatedCase.Edit_Counter = editCounter.get(s.ID);

            if(lastResponseDate.containsKey(s.ID) && (soqlCase.Last_Response_Date__c != lastResponseDate.get(s.ID)))           
                updatedCase.Last_Response_Date = lastResponseDate.get(s.ID);

            if(readyforAPdate.containsKey(s.ID) && (soqlCase.Ready_for_AP_Date__c != readyforAPdate.get(s.ID)))           
                updatedCase.Ready_for_AP_Date = readyforAPdate.get(s.ID);

            if(readyforOEdate.containsKey(s.ID) && (soqlCase.Ready_for_OE_Date__c != readyforOEdate.get(s.ID)))           
                updatedCase.Ready_for_OE_Date = readyforOEdate.get(s.ID);

            if(readyforQCdate.containsKey(s.ID) && (soqlCase.Ready_for_QC_Date__c != readyforQCdate.get(s.ID)))           
                updatedCase.Ready_for_QC_Date = readyforQCdate.get(s.ID);

            MapCase(soqlCase, updatedCase);                                                                                                                                             
                
            casesToUpdate.add(updatedCase);           
        }
                                
        if(casesToUpdate.size() > 0)
        {
            if(modifyObjects){
            	List<Case> caseObjs = new List<Case>();
            	for(MigratedCaseFields mc : casesToUpdate)
            		caseObjs.add(mc.ToCase());
            		
                update caseObjs;
                RebuildCaseTeam(caseObjs);
            }
            else
                email(casesToUpdate);
        }                               
    }
    
    global void RebuildCaseTeam(List<Case> caseObjs){
		CaseTeamHelper cth = new CaseTeamHelper();
		
		List<ID> ids = new List<ID>();
		for(Case c : caseObjs)
			ids.add(c.id);
		
		List<Case> casesWithTeamMembers = [Select Nato_Rep_SS__c, NATO_Rep_OV__c, Order_Entry__c, Ad_Production__c, Quality_Control__c, Nato_Rep_QC2__c, Billing__c from Case where ID in :ids];
		
		for(Case c : casesWithTeamMembers){
			cth.RemoveAllUsersFromCaseTeamRole(c.Id, 'Nato Case Team');
    		Set<ID> caseTeamMembers = new Set<ID>{
				c.Nato_Rep_SS__c, 
				c.NATO_Rep_OV__c,
				c.Order_Entry__c,
				c.Ad_Production__c,
				c.Quality_Control__c,
				c.Nato_Rep_QC2__c,
				c.Billing__c}; 
			caseTeamMembers.remove(null);	
	    	for(ID memberToAdd : caseTeamMembers)
		    	cth.AddCaseTeamMember(memberToAdd, c.Id, 'Nato Case Team', false);   		    					
		}			        	
		cth.SaveToDatabase();
    }
    global void email(List<MigratedCaseFields> mCases)
    {
    	Map<ID, string> allUsers = new Map<ID, string>();
        List<Group> queues = [select ID, Name from Group where Type = 'Queue'];
        List<User> users = [select Id, Name from User where IsActive = true];
        for(Group g : queues)
        	allUsers.put(g.ID, g.Name + ' [QUEUE]');
        for(User u : users)
        	allUsers.put(u.ID, u.Name + ' [USER]');
    	
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    
        mail.setToAddresses(new String[] {'salesforcedev@atti.com'});
        //mail.setToAddresses(new String[] {'arhemtulla@atti.com'});
        mail.setReplyTo('arhemtulla@atti.com');
        mail.setSenderDisplayName('Batch Processing');
        mail.setSubject('Init National Case Field Processing.');
    
        string body = '--- Start CSV ---\r\n';
        body += '"ID","Owner ID","Owner Name","OV REP","OV REP NAME","SS REP","SS REP NAME","Billing REP","Billing REP NAME","Ready for OV Date","QC Level","Billing Query Counter","Edit Counter","Query Counter",';
        body += '"Second QC Check","Ready for AP Date","Ready for OE Date","Ready for QC Date","Released Live Date","Last Query Date","Last Response Date","Released Live","Future Start","Keyed in AF"\r\n';

        string caseInfo;
        for(MigratedCaseFields m : mCases){
        		caseInfo = '"' + m.ID + '",';
        		caseInfo += '"' + m.OwnerID + '",';
        		
        		if(m.OwnerID != null && allUsers.ContainsKey(m.OwnerID))
        			caseInfo += '"' + allUsers.get(m.OwnerID) + '",';
        		else
        			caseInfo += ',';
        		
        		caseInfo += '"' + m.NATO_Rep_OV + '",';
        		if(m.NATO_Rep_OV != null && allUsers.ContainsKey(m.NATO_Rep_OV))
        			caseInfo += '"' + allUsers.get(m.NATO_Rep_OV) + '",';
        		else
        			caseInfo += ',';
        			
        		caseInfo += '"' + m.Nato_Rep_SS + '",';
        		if(m.Nato_Rep_SS != null && allUsers.ContainsKey(m.Nato_Rep_SS))
        			caseInfo += '"' + allUsers.get(m.Nato_Rep_SS) + '",';
        		else
        			caseInfo += ',';
        			
        		caseInfo += '"' + m.Billing + '",';
        		if(m.Billing != null && allUsers.ContainsKey(m.Billing))
        			caseInfo += '"' + allUsers.get(m.Billing) + '",';
        		else
        			caseInfo += ',';
        		
        		caseInfo += '"' + m.Ready_for_OV_Date + '",';
        		caseInfo += '"' + m.NATO_QC_Level + '",';
        		caseInfo += '"' + m.Billing_Query_Counter + '",';
        		caseInfo += '"' + m.Edit_Counter + '",';
        		caseInfo += '"' + m.Query_Counter + '",';
        		caseInfo += '"' + m.Second_QC_Check + '",';
        		caseInfo += '"' + m.Ready_for_AP_Date + '",';
        		caseInfo += '"' + m.Ready_for_OE_Date + '",';
        		caseInfo += '"' + m.Ready_for_QC_Date + '",';
        		caseInfo += '"' + m.Released_Live_Date + '",';
        		caseInfo += '"' + m.Last_Query_Date + '",';
        		caseInfo += '"' + m.Last_Response_Date + '",';
        		caseInfo += '"' + m.Released_Live + '",';
        		caseInfo += '"' + m.Future_Start + '",';
        		caseInfo += '"' + m.Keyed_in_AF_Checkbox + '"\r\n';
        		
        		body += caseInfo;
        }               
        
        mail.setPlainTextBody(body);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
    
    global void finish(Database.BatchableContext bc){
    }
    
    global Date ToDate(DateTime dt)
    {
        return Date.newInstance(dt.year(),dt.month(),dt.day());         
    }
    
    global class MigratedCaseFields{
        ID id;
        ID OwnerID;
        ID NATO_Rep_OV;
        ID Nato_Rep_SS;
        ID Billing;
        
        
        datetime Ready_for_OV_Date;
        decimal NATO_QC_Level;
        
        // History populated fields
        decimal Billing_Query_Counter;
        decimal Edit_Counter;
        decimal Query_Counter;
        
        datetime Second_QC_Check;
        datetime Ready_for_AP_Date;
        datetime Ready_for_OE_Date;       
        datetime Ready_for_QC_Date;       
        datetime Released_Live_Date;
        datetime Last_Query_Date;
        datetime Last_Response_Date;

            
        boolean Released_Live;
        boolean Future_Start;
        boolean Keyed_in_AF_Checkbox;
        
        global Case ToCase(){
        	Case c = new Case(ID = this.id);
        	if(OwnerID != null)
        		c.OwnerID = OwnerID;
        	if(NATO_Rep_OV != null)
		        c.NATO_Rep_OV__c = NATO_Rep_OV;
        	if(Nato_Rep_SS != null)
		        c.Nato_Rep_SS__c = Nato_Rep_SS;
        	if(Billing != null)
		        c.Billing__c = Billing;
        	if(Ready_for_OV_Date != null)
		        c.Ready_for_OV_Date__c = Ready_for_OV_Date;	        
        	if(NATO_QC_Level != null)
		        c.NATO_QC_Level__c = NATO_QC_Level;
		        
		        // History populated fields
        	if(Billing_Query_Counter != null)
		        c.Billing_Query_Counter__c = this.Billing_Query_Counter;
        	if(Edit_Counter != null)
		        c.Edit_Counter__c = Edit_Counter;
        	if(Query_Counter != null)
		        c.Query_Counter__c = Query_Counter;
		        
        	if(Second_QC_Check != null)
		        c.Second_QC_Check__c = Second_QC_Check;
        	if(Ready_for_AP_Date != null)
		        c.Ready_for_AP_Date__c = Ready_for_AP_Date;
        	if(Ready_for_OE_Date != null)
		        c.Ready_for_OE_Date__c = Ready_for_OE_Date;       
        	if(Ready_for_QC_Date != null)
		        c.Ready_for_QC_Date__c = Ready_for_QC_Date;       
        	if(Released_Live_Date != null)
		        c.Released_Live_Date__c = Released_Live_Date;
        	if(Last_Query_Date != null)
		        c.Last_Query_Date__c = Last_Query_Date;
        	if(Last_Response_Date != null)
		        c.Last_Response_Date__c = Last_Response_Date;
			            
        	if(Released_Live != null)
		        c.Released_Live__c = Released_Live;
        	if(Future_Start != null)
		        c.Future_Start__c = Future_Start;
        	if(Keyed_in_AF_Checkbox != null)
		        c.Keyed_in_AF_Checkbox__c = Keyed_in_AF_Checkbox;
        	
        	return c;
        }
    }
        
    global MigratedCaseFields MapCase(Case caseobj, MigratedCaseFields updatedCase){
        final string OEQueue = 'Order Entry Queue';
        final string OVQueue = 'Order Validation Queue';
        final string QCQueue = 'Quality Control Queue';
        final string SSQueue = 'National Sales Support Queue';
        final string APQueue = 'Ad Production Queue';
        final string BillingQueue = 'Billing Queue';
            
        updatedCase.Billing = billingUser;

        if(ovTeam.containsKey(caseObj.ownerId))
            updatedCase.NATO_Rep_OV = caseObj.ownerId;

        if(ssTeam.containsKey(caseObj.ownerId))
            updatedCase.Nato_Rep_SS = caseObj.ownerId;

        if(caseObj.Second_QC_Check__c != null || updatedCase.Second_QC_Check != null)
            updatedCase.NATO_QC_Level = 2;
        else
            updatedCase.NATO_QC_Level = 1;

        updatedCase.Ready_for_OV_Date = caseObj.CreatedDate;

		if(updatedCase.Released_Live_Date != null)
        	updatedCase.Released_Live = true;
        	
        if(caseObj.Future_Start_Date__c != null)
        	updatedCase.Future_Start = true;

		if(caseObj.Keyed_In_AF__c != null)        	
	        updatedCase.Keyed_in_AF_Checkbox = true;

        // Owner Mapping
        if((caseObj.Status == 'OE in Progress') ||
        (caseObj.Status == 'OE With Vendor') ||
        (caseObj.Status == 'QC Edit to OE')){
            if(caseObj.Order_Entry__c != null)
                updatedCase.OwnerID = caseObj.Order_Entry__c;
            else
                updatedCase.OwnerID = GetQueueID(OEQueue);  
        }

        if((caseObj.Status == 'Future Start Date') ||
        (caseObj.Status == 'Ready for OV')){
            if(updatedCase.NATO_Rep_OV != null)
                updatedCase.OwnerID = updatedCase.NATO_Rep_OV;
            else
                updatedCase.OwnerID = GetQueueID(OVQueue);  
        }


         
        if((caseObj.Status == 'QC in Progress') ||
        (caseObj.Status == 'Site Issue') ||
        (caseObj.Status == 'QC Released Live')){
            if(caseObj.Quality_Control__c != null)
                updatedCase.OwnerID = caseObj.Quality_Control__c;
            else
                updatedCase.OwnerID = GetQueueID(QCQueue);  
        }
         
        if((caseObj.Status == 'Query to Client') ||
        (caseObj.Status == 'OE Query to SS') ||
        (caseObj.Status == 'AP Query to SS') ||
        (caseObj.Status == 'QC Query to SS') ||
        (caseObj.Status == 'QC Fulfilled') ||
        (caseObj.Status == 'Billing Query to SS') ||
        (caseObj.Status == 'OV Pre-Fulfillment to SS') ||
        (caseObj.Status == 'Billing Complete')){
            if(updatedCase.Nato_Rep_SS != null)
                updatedCase.OwnerID = updatedCase.Nato_Rep_SS;
            else
                updatedCase.OwnerID = GetQueueID(SSQueue);  
        }

        if((caseObj.Status == 'AP in Progress') ||
        (caseObj.Status == 'QC Edit to AP')){
            if(caseObj.Ad_Production__c != null)
                updatedCase.OwnerID = caseObj.Ad_Production__c;
            else
                updatedCase.OwnerID = GetQueueID(APQueue);  
        }

        if(caseObj.Status == 'Ready for AP')
            updatedCase.OwnerID = GetQueueID(APQueue);  

        if((caseObj.Status == 'Ready for Billing') ||
        (caseObj.Status == 'SS Response to Billing'))
            updatedCase.OwnerID = GetQueueID(BillingQueue); 

        if(caseObj.Status == 'Ready for OE')
            updatedCase.OwnerID = GetQueueID(OEQueue);  
            
        if((caseObj.Status == 'Ready for QC') ||
        (caseObj.Status == 'Ready for 2nd QC'))
            updatedCase.OwnerID = GetQueueID(QCQueue);  
            
        return updatedCase;
    }
    global ID GetQueueID(string queueName){        
        return queueMap.get(queueName);
    }
}